using System;
using OpenTK.Audio.OpenAL;

namespace Clandestine.Audio
{
	internal class StreamingBuffer : Buffer
	{
		private Decoder decoder;
		private int sourceId;
		private int lastProcessedBuffersCount = 0;
		
		public const int PreferredDecodeSize = 32768; // liek 0.74s
		
		public bool Loop
		{
			get { return decoder.Loop; }
			set { decoder.Loop = value; }
		}
		
		public StreamingBuffer(Audio audio, string identifier, Decoder decoder, int sourceId)
			: base(audio, identifier)
		{
			this.decoder = decoder;
			this.sourceId = sourceId;
			
			//throw new NotImplementedException();
		}
		
		public void TearDown()
		{
			audio.StreamingThread.RemovePollee(this);
			dequeueAllBuffers();
		}
		
		public void PlayFromStart()
		{
			decoder.Rewind();
			enqueueBuffers();
			audio.StreamingThread.AddPollee(this);
		}
		
		private void enqueueBuffers()
		{
			for (int p = 0; p < 2 && (!decoder.EndOfStream); p++)
			{
				int bufid = AL.GenBuffer();
				queueBuffer(bufid);
			}
		}
		
		private void dequeueAllBuffers()
		{
			int c;
			
			while (true)
			{
				AL.GetSource(sourceId, ALGetSourcei.BuffersQueued, out c);
				
				if (c < 1)
					return;
				
				// Otherwise...
				int bufid = AL.SourceUnqueueBuffer(sourceId);
				AL.DeleteBuffer(bufid);
			}
		}
		
		internal override void Destroy()
		{
			dequeueAllBuffers();
			sourceId = 0;
			
			decoder.EndDecode();
		}
		
		internal bool Poll()
		{
			// StreamingThread.threadProc() handles the AL locking this time.
			int c;
			AL.GetSource(sourceId, ALGetSourcei.BuffersProcessed, out c);
			
			// We have two buffers for each source -- if one of them is finished with (so OpenAL is on the second one),
			//  dequeue the first (used) one, fill with fresh beats, and enqueue on the end.  Beautiful.
			//if (c - lastProcessedBuffersCount > 0)
			if (c > 0)
			{
				int bufferId = AL.SourceUnqueueBuffer(sourceId);
				
				if (!decoder.EndOfStream)
					queueBuffer(bufferId);
				
				//lastProcessedBuffersCount = c;
				c--;
			}
			
			return !decoder.EndOfStream;
		}
		
		private void queueBuffer(int bufferId)
		{
			byte[] chunk = decoder.DecodeChunkPCM16bit();
			
			if (chunk.Length == 0)  // Makes life easier for the Vorbisfile decoder.
				return;
			
			AL.BufferData(bufferId, (decoder.Channels == Channels.Stereo ? ALFormat.Stereo16 : ALFormat.Mono16),
			              chunk, chunk.Length, decoder.Rate);
			
			AL.SourceQueueBuffer(sourceId, bufferId);
		}
	}
}

